package fr.cephb.joperon.webapp;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Vector;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sleepycat.db.DatabaseException;

import fr.cephb.joperon.core.bio.Assembly;
import fr.cephb.joperon.core.bio.Chromosome;
import fr.cephb.joperon.core.db.SnpMapDB;
import fr.cephb.joperon.core.entities.SnpMap;
import fr.cephb.joperon.tool.OperonFactory;
import fr.cephb.util.CloseableIterator;

/**
 * A servlet displaying a map of SNP as SVG
 * @author operon
 *
 */
public class SnpMap2SVG extends HttpServlet
{
private static final long serialVersionUID = 1L;

private class SVGMapper
	{
	private Collection<SnpMap> snpList;
	int width;
	PrintWriter out;
	Chromosome chrom;
	int start;
	int end;
	SVGMapper(Collection<SnpMap> snpList,int width,PrintWriter out,Chromosome chrom,int start,int end)
		{
		this.snpList=snpList;
		this.out=out;
		this.width=width;
		this.chrom=chrom;
		this.start=start;
		this.end=end;
		}
	
	public int getWidth()
		{
		return this.width;
		}
	public int getFeatureHeight()
		{
		return 24;
		}
	
	public int getFontSize()
		{
		return 9;
		}
	
	public int getHeight()
		{
	   return this.snpList.size()* getFeatureHeight();
		}
	
	public double getLeftMarginWidth()
		{
		return getWidth()/5.0;
		}
	
	public double  getTrackMarginWidth()
		{
		return getLeftMarginWidth()/2.0;
		}
	
	public double  getItemMarginWidth()
		{
		return getLeftMarginWidth()/2.0;
		}
	
	public double getGenomeWidth()
		{
		return getWidth()-getLeftMarginWidth();
		}
	
	public double trimPixel(double pix)
		{
		pix= Math.min(getWidth(),pix);
		pix= Math.max(getLeftMarginWidth(),pix);
		return pix;
		}
	
	public double base2pixel(int base)
		{
		double left= getLeftMarginWidth();
		double screenw= getGenomeWidth();
		double pix= left+screenw* ((double)(base - this.start)/(double)(this.end -this.start));
		return pix;
		}
	void writeStrand(double pixx1,double pixx2,double midy)	
		{
		//write orientation
		for(double i=pixx1; i<= pixx2;i+=10)
			{
			out.print("<svg:use x='"+i+"' y='"+midy+"' xlink:href='#"+(Math.random()<0.5?"minus":"plus")+"'/>");
			}
		}

	void print()
		{
		float fh2=(float)getFeatureHeight()/2.0f;
		out.print( "<svg:svg xmlns:svg='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='"+ getWidth()+"' height='"+getHeight()+
					"' stroke='black' stroke-width='0.5' font-size='"+getFontSize()+"'>"+
				"<svg:title>"+chrom+":"+start+"-"+end+"</svg:title>"+
				"<svg:desc>Operon Browser with SVG Pierre Lindenbaum plindenbaum (@) yahoo (dot) fr </svg:desc>"+
				"<svg:defs>"+
				"<svg:polyline id='plus' stroke='black' fill='none' points='"+
					"-"+fh2+",-"+fh2+" "+
					" 0,0 "+
					"-"+fh2+","+fh2+"'/>"+
				"<svg:polyline id='minus' stroke='black' fill='none' points='"+
					""+fh2+",-"+fh2+" "+
					" 0,0 "+
					""+fh2+","+fh2+"'/>"+
				"<svg:linearGradient x1='0%' y1='0%' x2='0%' y2='100%' id='metal'>\n"+
				"<svg:stop offset=\"5%\" stop-color=\"black\"/>\n"+
				"<svg:stop offset=\"50%\" stop-color=\"whitesmoke\"/>\n"+
				"<svg:stop offset=\"95%\" stop-color=\"black\"/>\n"+
				"</svg:linearGradient>\n"+
				"</svg:defs>"
				);
			out.print("<svg:rect  x='0' y='0' "+
				"width='"+ getWidth() +"' height='"+getHeight()+"' stroke='blue' fill='white' "+
				"/>"
				);
			
			//write vertical bar
			out.print("<svg:g>");
			for(int i=1;i<10;i++)
				{
				double x= getLeftMarginWidth() +(getGenomeWidth()/10.0)*i;
				out.print("<svg:line x1='"+x+"' y1='0' x2='"+x+"' y2='"+getHeight()+"' stroke='blue' />");
				}
			out.print("</svg:g>");	
			
			int y=0;
			out.print(	"<svg:g>"+
					
					"<svg:rect x='0' y='"+y+"' width='"+getTrackMarginWidth()+
					"' height='"+getHeight()+"' fill='rgb(240,240,255)'/>"+
					"<svg:text x='0' y='0' text-anchor='middle'  transform='translate("+((getTrackMarginWidth())/2.0)+","+(y+getFontSize()/2.0+(getHeight())/2.0)+") rotate(0)'>SnpMap</svg:text>"+
					"</svg:g>\n"
					);
			out.print("<svg:rect x='"+getTrackMarginWidth()+"' y='"+y+"' width='"+getItemMarginWidth()+
				"' height='"+getHeight()+"' fill='rgb(200,200,255)'/>\n");
			
			
			
			for(SnpMap snp: snpList)
				{
				out.print(snp.getAcn().startsWith("rs")?"<svg:a xlink:href='http://www.ncbi.nlm.nih.gov/SNP/snp_ref.cgi?rs="+ snp.getAcn().substring(2)+"' xlink:title='"+snp.getAcn()+"'>":"");
				out.print("<svg:g>\n");
				
		
				
				out.print(		"<svg:text  x='"+
						(getTrackMarginWidth()+(getItemMarginWidth()/2.0))+"' y='"+
						(y+getFeatureHeight()-getFontSize()/2.0)+"' text-anchor='middle'>"+
						snp.getAcn()+" : "+snp.getFid().getPosition()+
						"</svg:text>"
						
						);
					
				
				double pixx1= (base2pixel(snp.getFid().getPosition()));
				double pixx2= (base2pixel(snp.getFid().getPosition()+1));
				double midy= (y+getFeatureHeight()/2.0);
				
				//write orientation
				writeStrand(pixx1,pixx2,midy);
				double h2= getFeatureHeight()/5.0;
				out.print(
						"<svg:rect x='"+pixx1+"'  y='"+(midy-h2)+"'  width='"+(pixx2-pixx1)+"'   height='"+(h2*2)+"' style='stroke:red;fill:url(#metal);'/>"
						);
				
				out.print("</svg:g>");
				out.print(snp.getAcn().startsWith("rs")?"</svg:a>":"");
				y+= getFeatureHeight();
				}
			
				
			
				
			
				
			
			out.print("<svg:rect  x='0' y='0' "+
				"width='"+width+"' height='"+getHeight()+"' stroke='blue' fill='none' "+
				"/>"
				);
			out.print( "</svg:svg>");
			out.flush();
		}
	}


@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
		throws ServletException, IOException
	{

	SnpMapDB db= null;
	try {
		Integer start= (Integer) req.getAttribute("start") ;
		Integer end= (Integer) req.getAttribute("end") ;
		Chromosome chrom= (Chromosome) req.getAttribute("chrom") ;
		Assembly assembly= (Assembly) req.getAttribute("assembly") ;
		OperonFactory factory= (OperonFactory)req.getAttribute("factory");
		PrintWriter out = res.getWriter();
		
		db= factory.getSnpMap(assembly);
		Vector<SnpMap> snpList= new Vector<SnpMap>();
		
		if(chrom!=null && chrom!=Chromosome.ChrUndefined && start!=null && end!=null)
			{
			CloseableIterator<SnpMap> iter=db.listBetween(
					chrom, start, end);
			while(iter.hasNext())
				{
				SnpMap m= iter.next();
				snpList.addElement(m);
				if(snpList.size()>10000) break;
				}
			iter.close();
			}
		
		
		int width=1000;
		if(chrom!=null && chrom!=Chromosome.ChrUndefined && start!=null && end!=null && !snpList.isEmpty())
			{
			res.setContentType("image/svg+xml");
			SVGMapper mapper= new SVGMapper(snpList,width,out,chrom,start,end);
			mapper.print();
			}
		else
			{
			res.setContentType("text/html");
			out.println("No Data");
			}
		
	} catch (DatabaseException e)
		{
		throw new ServletException(e);
		}
	
	}

static float base2pixel(int pos, int chromStart,int chromEnd,int screenWidth)
	{
	return pos;
	}

}
